import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.rmi.*;
import java.rmi.server.*;
import java.security.Key;
import java.util.Random;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class DirectoryService extends UnicastRemoteObject implements DirectoryServiceInterface {

	private static final String ALGORITHM = "AES";
	
	private String mysecretkey;
	private String restofpath;
	private String[][] table;
	private String[][] connections;
	private String configfilepath = "/home/mark/Desktop/DistSysStuff/configfile";
	
	public DirectoryService() throws RemoteException{
		/*Need to sort out browser config page!!*/
		
		mysecretkey = "My super amazing secret DIR key!";
		try {
			Runtime.getRuntime().exec("google-chrome localhost/mypage.php");
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}	
	
	/*First of all the method tries to match the given path with one in the directory table.
	 * If it's not matched directly then it moves up the directory hierarchy, to see if a parent
	 * directory is mapped in the directory table. If it is, then it returns what the parent directory
	 * is mapped to and THEN it adds to that the sub-directories that could not be mapped.
	 * This assumes that the directory layout must be the same*/
	
	/*
	 * Expects in token:
	 * [0]: DIR_SEC( ClientID )
	 * [1]: DIR_SEC( SessionKey )
	 * 
	 * Expects path to be encrypted with session key
	 */
	public String[] resolveEncr(String path, String[] token) throws RemoteException{
		try {
			String clientID = decrypt( token[0], mysecretkey.getBytes() );
			String sessionKey = decrypt( token[1], mysecretkey.getBytes());
			
			String p = decrypt (path, sessionKey.getBytes());
			
			String[] res = resolve(p);
			//Encrypt result
			for(int i = 0; i < res.length; i++){
				res[i] = encrypt( res[i], sessionKey.getBytes());
			}
			return res;
		
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public String[] resolve(String path) throws RemoteException {
		String[] res = new String[2];
		res[0] = ""; res[1] = "";
		String client;
		try {
			client = "//" + RemoteServer.getClientHost();
			loadConfig();
			restofpath = "";
			String p = path;
			boolean first_it = true;
			while(res[0].equals("") && p.length() > 0){
	
				
				if(!first_it){
					p = moveUpPath(p);
				}
				first_it = false;
				System.out.println("DIRSERV: Looking for CLIENT: " + client + ", FILE: " + path);
				for(int i = 0; i < table.length; i++){
					System.out.println("Comparing: " + client+" "+path+ "  to  " +table[i][0] + " " + table[i][1]);
					if(table[i][0].equalsIgnoreCase(client) && table[i][1].equalsIgnoreCase(path)){
						res[0] = table[i][2];
						res[1] = table[i][3];
					}
				}
				System.out.println("Moving onto path: " + p);
			}
			res[1] = res[1] + restofpath;
		} catch (ServerNotActiveException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return res;
	}
	
	private String moveUpPath(String path){
		int i;
		for(i = path.length() - 1;i > 0 && path.charAt(i) != '/' ; i--){
			restofpath = path.charAt(i) + restofpath;
			System.out.println("DS: movUpPath() i:" + i);
		}
		restofpath = path.charAt(i) + restofpath;
		return path.substring(0, i);
	}

	//Loads the mappings in the config file into 2D string array 'table'
	private void loadConfig(){
		File f = new File(configfilepath);
		/*
		 * Example format of a line in the config file is:
		 * //127.0.1.1:/home/mark/clientfolder //127.0.1.1:/home/mark/serverfolder
		 * The left side is the Client location, the right side is the Server location
		 */
		try {
			//Initialises the 'table'
			int  mapcount = countLines(f);
			table = new String[mapcount][];
			for(int i = 0; i < table.length; i++){
				table[i] = new String[4];
			}
			BufferedReader brd = new BufferedReader(new FileReader(f));
			//Iterates through each line of the config file
			for(int i = 0; brd.ready(); ++i){
				String map = brd.readLine();
				String[] twodir = map.split(" ");
				String[][] parsed = new String[2][];
				parsed[0] = twodir[0].split(":");
				parsed[1] = twodir[1].split(":");
				
				table[i][0] = parsed[0][0];
				table[i][1] = parsed[0][1];
				table[i][2] = parsed[1][0];
				table[i][3] = parsed[1][1];		
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	//Counts the amount of lines in the configfile so we know how big to make the mapping table
	private int countLines(File f){
		int count = 0;
		try {
			InputStream is = new BufferedInputStream(new FileInputStream(f));
			byte[] c = new byte[1024];
			int readChars = 0;
			while((readChars = is.read(c)) != -1){
				for(int i = 0; i < readChars; ++i){
					if(c[i] == '\n')
						++count;
				}
			}
			is.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return count;
	}
	
	private String encrypt(String msg, byte[] keyValue) throws Exception{
		Key key = new SecretKeySpec(keyValue, ALGORITHM);
		Cipher c = Cipher.getInstance(ALGORITHM);
		c.init(Cipher.ENCRYPT_MODE, key);
		
		String valueToEnc = msg;
		byte[] encValue = c.doFinal(new BASE64Decoder().decodeBuffer(valueToEnc));
		String eValue = new BASE64Encoder().encode(encValue);
		return eValue;
	}
	
	private String decrypt(String msg, byte[] keyValue) throws Exception{
		Key key = new SecretKeySpec(keyValue, ALGORITHM);
		Cipher c = Cipher.getInstance(ALGORITHM);
		c.init(Cipher.DECRYPT_MODE, key);
		
		byte[] decordedValue = new BASE64Decoder().decodeBuffer(msg);
		byte[] decValue = c.doFinal(decordedValue);
		String dValue = new String(decValue);
		return dValue;
	}
	
}